package server;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.Node;
import org.dom4j.io.SAXReader;

import java.io.*;
import java.net.MalformedURLException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URL;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.*;

/**
 * Minicat主类
 */
public class Bootstrap {

    //用来存储servlet key url-pattern
    private Map<String, HttpServlet> servletMap = new HashMap<>();


    //定义监听端口
    private int port = 8080;

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }


    public void start() throws Exception {

        // 加载解析相关的配置，server.xml
        loadServerXml();


        ServerSocket serverSocket = new ServerSocket(port);
        System.out.println("======>>>Minicat start on port :" + port);

        /*
            完成Minicat 1.0版本
            需求：浏览器请求http://localhost:8080,返回一个固定的字符串到页面"Hello Minicat!"
         */
       /* while (true) {
            Socket socket = serverSocket.accept();
            //获取输出流 输出页面信息
            OutputStream outputStream = socket.getOutputStream();
            String data = "Hello Minicat!";
            String responseText = HttpProtocolUtil.getHttpHeader200(data.getBytes().length) + data;
            outputStream.write(responseText.getBytes());
            socket.close();

        }*/

        /**
         * 完成Minicat 2.0版本
         * 需求：封装Request和Response对象，返回html静态资源文件
         */
       /* while (true) {
            Socket socket = serverSocket.accept();
            //获取输入流
            InputStream inputStream = socket.getInputStream();
            Request request=new Request(inputStream);
            Response response=new Response(socket.getOutputStream());
            response.outputHtml(request.getUrl());
            socket.close();
        }*/


        /**
         * 完成Minicat 3.0版本
         * 需求：封装Request和Response对象，返回html静态资源文件
         */
        /*while (true) {
            Socket socket = serverSocket.accept();
            //获取输入流
            InputStream inputStream = socket.getInputStream();
            Request request=new Request(inputStream);
            Response response=new Response(socket.getOutputStream());
            //判断请求的是否是静态文件
            if (   servletMap.get(request.getUrl())==null){
                //servlet集合中没有对应的servlet 证明请求的是静态资源
                response.outputHtml(request.getUrl());
            }else {
                HttpServlet httpServlet = servletMap.get(request.getUrl());
                httpServlet.service(request, response);
            }
            response.outputHtml(request.getUrl());
            socket.close();
        }*/

        // 多线程改造  不使用线程池
      /*  while (true) {
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor=new RequestProcessor(socket, servletMap);
            requestProcessor.start();
        }*/

        //初始化线程数量
        int corePoolSize = 10;
        //线程池最大线程数量
        int maximumPoolSize = 50;
        //线程持续时间
        long keepAliveTime = 100L;
        //时间单位
        TimeUnit unit = TimeUnit.SECONDS;
        //队列
        BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
        //线程工厂 使用默认即可
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        //线程执行策略
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();

        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler
        );
        // 使用线程池
        while (true) {
            Socket socket = serverSocket.accept();
            RequestProcessor requestProcessor = new RequestProcessor(socket, servletMap);
            threadPoolExecutor.execute(requestProcessor);
        }

    }

    /**
     * Minicat 4.0版本
     * 加载解析server.xml 初始化servlet
     */
    private void loadServerXml() {
        //读取server.xml 加载进内存
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("server.xml");
        //读取解析 流
        SAXReader saxReader = new SAXReader();
        try {
            Document document = saxReader.read(resourceAsStream);
            /*<Server >
            <app appBase = "E:/lagouCode/stage2/module1/Minicat/webapps" / >
            </Server >*/
            //获取根标签 <Server>
            Element rootElement = document.getRootElement();
            Element app = (Element) rootElement.selectSingleNode("//app");
            //获取到配置的app目录
            String appBase = app.attributeValue("appBase");
            System.out.println("部署在Minicat上的app目录为======>>>> " + appBase);

            //遍历此目录 初始化server
            File file = new File(appBase);
            if (!file.exists() || !file.isDirectory()) {//如果文件目录不存在 或者 不为文件夹 给出提示
                System.out.println("======>>>>server.xml中的appBase目录不存在或不是目录" + appBase);

            } else {
                File[] files = file.listFiles();
                for (File appFile : files) {
                    if (appFile.isDirectory()) {
                        String context = appFile.getName();
                        loadServlet(context, appFile);
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    /**
     * 加载解析web.xml，
     */
    private void loadServlet(String context, File appFile) {

        //将web.xml 以流的形式加载到内存
        //  InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");
        //获取每个项目的web.xml
        String webXmlPath = appFile.getAbsolutePath() + "/WEB-INF/web.xml";
        //判断是否存在
        File file = new File(webXmlPath);
        if (!file.exists() || !file.isFile()) {
            return;
        }
        //加载进内存
        InputStream inputStream = null;

        SAXReader saxReader = new SAXReader();
        try {
            inputStream = new FileInputStream(file);
            Document document = saxReader.read(inputStream);
            Element rootElement = document.getRootElement();

            List<Element> selectNodes = rootElement.selectNodes("//servlet");
            for (int i = 0; i < selectNodes.size(); i++) {
                Element element = selectNodes.get(i);
                // <servlet-name>gc</servlet-name>
                Element servletNameElement = (Element) element.selectSingleNode("servlet-name");
                String servletName = servletNameElement.getStringValue();
                // <servlet-class>server.GcServlet</servlet-class>
                Element servletClassElement = (Element) element.selectSingleNode("servlet-class");
                String servletClass = servletClassElement.getStringValue();

                //根据servlet-name 的值找到对应的url-pattern
                Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" + servletName + "']");
                // /gc
                String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
                //拼接每个项目的访问路径  / + content + gc
                urlPattern = "/" + context + urlPattern;

                //自定义类加载器加载webapps下的class文件
                URL[] urls = new URL[0];
                WebappClassLoader webappClassLoader = new WebappClassLoader(urls, Thread.currentThread().getContextClassLoader());
                File classFile=new File(appFile.getAbsolutePath() + "\\WEB-INF\\classes");

                URL url = (classFile.toURI()).toURL();
                webappClassLoader.addURL(url);
                //E:\lagouCode\stage2\module1\Minicat\webapps\web_demo01_war_exploded\WEB-INF\classes\com\gc\servlet
                servletMap.put(urlPattern, (HttpServlet) webappClassLoader.loadClass(servletClass).getConstructor().newInstance());

                //当前项目无法加载 web项目的class
                // servletMap.put(urlPattern, (HttpServlet) Class.forName(servletClass).newInstance());

            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 程序入口
     *
     * @param args
     */
    public static void main(String[] args) {
        Bootstrap bootstrap = new Bootstrap();
        try {
            //启动Minicat
            bootstrap.start();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}
